/**
 * HappyFarm v2
 *
 * Util package
 *
 * Author: Cong Liu
 * Date: 03/07/2009
 *
 **/

(function(){

const Cc = Components.classes,
      Ci = Components.interfaces;

HappyFarm.util = {
    /**
     * log message into console
     *
     * @param String type       log message type
     * @param String message    message body
     *
     **/
    log: function(type, message){
        if(HF_DEBUG_ON || type !== 'Debug') {
            Application.console.log('[HappyFarm Toy]['+(new Date()).toLocaleString()+']['+type+'] '+this.hfdumpobj(message));
        }
    },
    hfdumpobj: function(obj){
        switch(typeof obj){
            case 'object':
                if(obj == null) return obj;
                if(obj instanceof Array){
                    return '[' + [this.hfdumpobj(p) for each(p in obj)].join(', ') + ']';
                } else if(obj instanceof XML) {
                    return obj.toXMLString();
                } else if(obj instanceof Date) {
                    return obj.toLocaleString();
                } else {
                    return '{' + [this.hfdumpobj(p)+': '+this.hfdumpobj(obj[p]) for(p in obj)].join(', ') + '}';
                }
                break;
            case 'function':
                return obj.toSource();
                break;
            case 'string':
                return '"' + obj.replace(/"/g, '\\"') + '"';
                break;
            case 'undefined':
            case 'boolean':
            case 'number':
            default:
                return obj;
                
        }
    },
    /**
     * extend object
     *
     * @param Object target     target object
     * @param Object ...        source objects
     *
     **/
    extend: function(target) {
        target = target || {};
        for(var i = 1; i < arguments.length; i++) {
            var from = arguments[i];
            if(from != null) {
                for(var prop in from) {
                    target[prop] = from[prop];
                }
            }
        }
        return target;
    },
    /**
     * Throw exception
     *
     * @param Exception aE      message
     **/
    hfException: function(aE) {
        if(typeof aE == 'string') {
            var errMsg = aE;
        } else {
            var m, n, r, l, ln, fn = "";
            try {
                rv = -aE.result;
                m  = aE.message;
                fn = aE.filename;
                l  = aE.location; 
                ln = l.lineNumber; 
            } catch (e) { }
            var errMsg ="FileName:          "+fn+"\n"           +
                        "Result:            "+rv+"\n"           +
                        "Message:           "+m+"\n"            +
                        "LineNumber:        "+ln+"\n";
        }
	    this.log('Exception', errMsg);
    	throw errMsg;
    },
    hfCreateInstance: function(aURL, aInterface) {
        var rv;
        try {
            rv = Cc[aURL].createInstance(Ci[aInterface]);
        } catch (e) { hfException(e); }
  
        return rv;
    },
    hfGetService: function (aURL, aInterface) {
        var rv;
        try {
            // determine how 'aInterface' is passed and handle accordingly
            switch (typeof(aInterface))
            {
                case "object":
                rv = Cc[aURL].getService(aInterface);
                break;

                case "string":
                rv = Cc[aURL].getService(Ci[aInterface]);
                break;

                default:
                rv = Cc[aURL].getService();
                break;
            }
        } catch (e) { hfException(e); }

        return rv;
    },
    /**
     * make an XHR request
     *
     * @param Object params     parameters to make the request
     *
     **/
    request: function(params) {
        var s = this.extend({
            url: '',
            data: {},
            method: 'GET',
            timeout: 0,
            complete: function(){},
        }, params);

        var headers = [];
        var method = s.method.toUpperCase();

        if(method == 'POST') {
            if(s.data) {
                var data = encodeData(s.data);
            } else {
                var data = null;
            }
            var url = s.url;
            headers.push(['Content-Length', data?data.length:0]);
            headers.push(['Content-Type', 'application/x-www-form-urlencoded']);
        } else {
            var url = s.url.replace(/\s+$/, '');
            var dataStr = encodeData(s.data);
            if(dataStr.length && url.indexOf('?') < 0) url += '?';
            url += dataStr;
            if(dataStr.length && url.slice(-1) != '&') url += '&';
            var data = null;
        }

        this.log('DEBUG', method+' '+url+(method=='POST'?'\n'+data:''));

        var xhr = new XMLHttpRequest();
        xhr.onload = complete;
        xhr.onerror = complete;
        xhr.open(method, url, true);
        for each(var h in headers) xhr.setRequestHeader(h[0], h[1]);
        xhr.send(data);

        var timeout = false;
        if(s.timeout > 0) {
            setTimeout(function(){
                timeout = true;
            }, s.timeout);
        }

        return xhr;

        function complete() {
            if(timeout) var status = 'timeout';
            else {
                if(xhr.status == 200) var status = 'success';
                else var status = 'error';
            }
            
            s.complete(status, xhr);
        }

        function encodeData(data) {
            return [encodeURIComponent(p)+'='
                            +(s.data[p]!==undefined?encodeURIComponent(s.data[p]):'')
                            for(p in s.data)].join('&');
        }
    },
    hex_md5: function(str){
        var converter = this.hfCreateInstance(
                "@mozilla.org/intl/scriptableunicodeconverter",
                "nsIScriptableUnicodeConverter");

        converter.charset = "UTF-8";
        // result is an out parameter,
        // result.value will contain the array length
        var result = {};
        // data is an array of bytes
        var data = converter.convertToByteArray(str, result);
        var ch = this.hfCreateInstance("@mozilla.org/security/hash;1",
                                        "nsICryptoHash");
        ch.init(ch.MD5);
        ch.update(data, data.length);
        var hash = ch.finish(false);

        // convert the binary hash data to a hex string.
        var s = [("0" + hash.charCodeAt(i).toString(16)).slice(-2) 
            for (i in hash)].join("");

        return s;
    },
    getPref: function(key, defaultValue) {
        var ext = Application.extensions.get(HappyFarm.ID);
        return ext.prefs.getValue(key, defaultValue);
    },
    setPref: function(key, value) {
        var ext = Application.extensions.get(HappyFarm.ID);
        return ext.prefs.setValue(key, value);
    }
}

})();
